package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.bind.annotation.ExceptionHandler;
import cc.shacocloud.mirage.utils.collection.SelfSortList;
import cc.shacocloud.mirage.utils.comparator.AnnotationOrderComparator;
import cc.shacocloud.mirage.utils.comparator.Ordered;
import io.vertx.core.Future;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 一个{@link AbstractHandlerExceptionResolver}，它通过{@link ExceptionHandler}方法解决异常。
 *
 * @see ExceptionHandlerMethodResolver
 */
public class ExceptionHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
    
    private final List<ExceptionHandlerMethodResolver> exceptionHandlerResolvers = new SelfSortList<>(AnnotationOrderComparator.INSTANCE::getOrder);
    
    private final Map<Class<?>, InvocableHandlerMethod> exceptionMethodHandlerCache =
            new ConcurrentHashMap<>(128);
    
    /**
     * 注册异常映射处理器
     *
     * @param exceptionHandler 异常处理器类
     */
    public void registerExceptionHandler(Object exceptionHandler) {
        ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(exceptionHandler);
        if (resolver.hasExceptionMappings()) {
            this.exceptionHandlerResolvers.add(resolver);
        }
    }
    
    @Override
    protected Future<Object> doResolveException(@NotNull HttpRequest request,
                                                @NotNull HttpResponse response,
                                                @NotNull VertxInvokeHandler handler,
                                                @NotNull Throwable cause) {
        InvocableHandlerMethod handlerMethod = getExceptionHandlerMethod(cause);
        
        // 这边返回空交给下一个处理器处理
        if (handlerMethod == null) return Future.succeededFuture(SKIP);
        
        // 先执行异常方法获取结果
        return handler.invokeAndHandleHandlerMethod(request.context(), handlerMethod, cause, cause.getCause(), handler.getHandler())
                // 在处理结果
                .compose(res -> handler.resultHandle(res, handlerMethod, response).map(r -> r));
    }
    
    /**
     * 为给定异常找到一个{@code @ExceptionHandler}方法并且封装{@link InvocableHandlerMethod}
     */
    @Nullable
    protected InvocableHandlerMethod getExceptionHandlerMethod(@NotNull Throwable cause) {
        
        InvocableHandlerMethod handlerMethod = this.exceptionMethodHandlerCache.get(cause.getClass());
        if (handlerMethod == null) {
            
            for (ExceptionHandlerMethodResolver exceptionHandlerResolver : exceptionHandlerResolvers) {
                Method method = exceptionHandlerResolver.resolveMethodByThrowable(cause);
                if (method != null) {
                    handlerMethod = new InvocableHandlerMethod(exceptionHandlerResolver.getExceptionHandler(), method);
                    this.exceptionMethodHandlerCache.put(cause.getClass(), handlerMethod);
                    break;
                }
            }
        }
        return handlerMethod;
    }
    
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}
